/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.screw.mapping;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.experimental.UtilityClass;
import org.elsfs.cloud.common.util.exception.ConvertException;

/**
 * 映射器
 *
 * @author zeng
 */
@UtilityClass
public class Mapping {

  /**
   * 将ResultSet 结果转为对象
   *
   * @param <T> 领域泛型
   * @param resultSet {@link ResultSet} 对象
   * @param clazz 领域类型
   * @return 领域对象
   * @throws ConvertException ConvertException
   */
  public <T> T convert(ResultSet resultSet, Class<T> clazz) throws ConvertException {
    // 存放列名和结果
    Map<String, Object> values = new HashMap<>(16);
    try {
      // 处理 ResultSet
      ResultSetMetaData metaData = resultSet.getMetaData();
      int columnCount = metaData.getColumnCount();
      // 迭代
      while (resultSet.next()) {
        // 放入内存
        for (int i = 1; i <= columnCount; i++) {
          String columnName = metaData.getColumnName(i);
          values.put(columnName, resultSet.getString(columnName));
        }
      }
      // 有结果
      if (!values.isEmpty()) {
        // 获取类数据
        List<FieldMethod> fieldMethods = getFieldMethods(clazz);
        // 设置属性值
        return getObject(clazz, fieldMethods, values);
      }
      return clazz.getDeclaredConstructor().newInstance();
    } catch (Exception e) {
      throw new ConvertException(e);
    }
  }

  /**
   * 将ResultSet 结果转为对象集合
   *
   * @param resultSet {@link ResultSet} 对象
   * @param clazz 领域类型
   * @param <T> 领域泛型
   * @return 领域对象
   * @throws ConvertException ConvertException
   */
  public <T> List<T> convertList(ResultSet resultSet, Class<T> clazz) throws ConvertException {
    // 存放列名和结果
    List<Map<String, Object>> values = new ArrayList<>(16);
    // 结果集合
    List<T> list = new ArrayList<>();
    try {
      // 处理 ResultSet
      ResultSetMetaData metaData = resultSet.getMetaData();
      int columnCount = metaData.getColumnCount();
      // 迭代
      while (resultSet.next()) {
        // map object
        HashMap<String, Object> value = new HashMap<>(16);
        // 循环所有的列，获取列名，根据列名获取值
        for (int i = 1; i <= columnCount; i++) {
          String columnName = metaData.getColumnName(i);
          value.put(columnName, resultSet.getString(i));
        }
        // add object
        values.add(value);
      }
      // 获取类数据
      List<FieldMethod> fieldMethods = getFieldMethods(clazz);
      // 循环集合，根据类型反射构建对象
      for (Map<String, Object> map : values) {
        T rsp = getObject(clazz, fieldMethods, map);
        list.add(rsp);
      }
    } catch (Exception e) {
      throw new ConvertException(e);
    }
    return list;
  }

  /**
   * 根据列标签获取列信息
   *
   * @param resultSet {@link ResultSet} 对象
   * @param clazz 领域类型
   * @param <T> 领域泛型
   * @return 领域对象
   * @throws ConvertException ConvertException
   */
  public static <T> List<T> convertListByColumnLabel(ResultSet resultSet, Class<T> clazz)
      throws ConvertException {
    // 存放列名和结果
    List<Map<String, Object>> values = new ArrayList<>(16);
    // 结果集合
    List<T> list = new ArrayList<>();
    try {
      // 处理 ResultSet
      ResultSetMetaData metaData = resultSet.getMetaData();
      int columnCount = metaData.getColumnCount();
      // 迭代
      while (resultSet.next()) {
        // map object
        HashMap<String, Object> value = new HashMap<>(16);
        // 循环所有的列，获取列名，根据列名获取值
        for (int i = 1; i <= columnCount; i++) {
          String columnName = metaData.getColumnLabel(i);
          value.put(columnName, resultSet.getString(i));
        }
        // add object
        values.add(value);
      }
      // 获取类数据
      List<FieldMethod> fieldMethods = getFieldMethods(clazz);
      // 循环集合，根据类型反射构建对象
      for (Map<String, Object> map : values) {
        T rsp = getObject(clazz, fieldMethods, map);
        list.add(rsp);
      }
    } catch (Exception e) {
      throw new ConvertException(e);
    }
    return list;
  }

  /**
   * 获取对象
   *
   * @param clazz class
   * @param fieldMethods List
   * @param map 数据集合
   * @param <T> 领域泛型
   * @return 领域对象
   * @throws InstantiationException InstantiationException
   * @throws IllegalAccessException IllegalAccessException
   * @throws InvocationTargetException InvocationTargetException
   */
  private static <T> T getObject(
      Class<T> clazz, List<FieldMethod> fieldMethods, Map<String, Object> map)
      throws InstantiationException,
          IllegalAccessException,
          InvocationTargetException,
          NoSuchMethodException {
    T rsp = clazz.getDeclaredConstructor().newInstance();
    // 设置属性值
    for (FieldMethod filed : fieldMethods) {
      Field field = filed.getField();
      Method method = filed.getMethod();
      MappingField jsonField = field.getAnnotation(MappingField.class);
      if (!Objects.isNull(jsonField)) {
        String value = jsonField.value();
        Object o = map.get(value);
        if (o == null) {
          o = map.get(value.toUpperCase());
        }
        if (o == null) {
          o = map.get(value.toLowerCase());
        }

        method.invoke(rsp, o);
      }
    }
    return rsp;
  }

  /**
   * 根据类型获取 FieldMethod
   *
   * @param clazz 对象
   * @param <T> t泛型
   * @return list
   * @throws IntrospectionException IntrospectionException
   * @throws NoSuchFieldException NoSuchFieldException
   */
  private static <T> List<FieldMethod> getFieldMethods(Class<T> clazz)
      throws IntrospectionException, NoSuchFieldException {
    // 结果集合
    List<FieldMethod> fieldMethods = new ArrayList<>();
    // BeanInfo
    BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    // 循环处理值
    for (PropertyDescriptor pd : pds) {
      Method writeMethod = pd.getWriteMethod();
      if (writeMethod == null) {
        continue;
      }
      // 获取字段
      Field field = clazz.getDeclaredField(pd.getName());
      // 获取只写方法
      FieldMethod fieldMethod = new FieldMethod();
      fieldMethod.setField(field);
      fieldMethod.setMethod(writeMethod);
      // 放入集合
      fieldMethods.add(fieldMethod);
    }
    return fieldMethods;
  }

  /**
   * 尝试获取属性
   *
   * <p>不会抛出异常，不存在则返回null
   *
   * @param clazz {@link Class}
   * @param itemName {@link String}
   * @return {@link Field}
   */
  private static Field tryGetFieldWithoutExp(Class<?> clazz, String itemName) {
    try {
      return clazz.getDeclaredField(itemName);
    } catch (Exception e) {
      return null;
    }
  }

  /**
   * 获取属性设置属性
   *
   * @param clazz {@link Class}
   * @param field {@link Field}
   * @return {@link Method}
   */
  private static <T> Method tryGetSetMethod(Class<T> clazz, Field field, String methodName) {
    try {
      return clazz.getDeclaredMethod(methodName, field.getType());
    } catch (Exception e) {
      return null;
    }
  }
}
